/**
 * @file bind_core.c
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019. All rights reserved.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * Description: bind core function
 * Author: huawei
 * Create: 2020-11-16
 */

#include "bind_core.h"

static int get_irq_cpumask(struct cpumask *irq_cpumask, const char *irq_node_name, const char *irq_cpu_name)
{
    int property_cpu_num;
    int i;
    struct device_node *irq_cpu_node = NULL;
    struct device_node *irq_affinity_hint_node = NULL;
    int err;
    unsigned int irq_cpu;
    unsigned int online_cpu_num = num_online_cpus();

    irq_affinity_hint_node = of_find_node_by_name(NULL, "irq_affinity_hint");
    if (irq_affinity_hint_node == NULL) {
        bind_core_err("get irq_affinity_hint_node failed.\n");
        return -1;
    }

    irq_cpu_node = of_find_node_by_name(irq_affinity_hint_node, irq_node_name);
    if (irq_cpu_node == NULL) {
        bind_core_err("get irq_cpu_node \"%s\" failed.\n", irq_node_name);
        return -1;
    }

    property_cpu_num = of_property_count_u32_elems(irq_cpu_node, irq_cpu_name);
    if (property_cpu_num < 0) {
        bind_core_err("property \"%s\" has no valid irq_cpu, errcode=%d\n", irq_cpu_name, property_cpu_num);
        return property_cpu_num;
    }

    for (i = 0; i < property_cpu_num; i++) {
        err = of_property_read_u32_index(irq_cpu_node, irq_cpu_name, i, &irq_cpu);
        if (err < 0) {
            bind_core_err("get irq_cpu failed, errcode=%d\n", err);
            return err;
        }
        if (irq_cpu >= online_cpu_num) {
            bind_core_err("cpu %d is out of online_cpu_num, bind irq to it failed.\n", irq_cpu);
            continue;
        }
        cpumask_set_cpu(irq_cpu, irq_cpumask);
    }

    bind_core_info("get irq_cpumask succ.\n");
    return 0;
}

/**
 * Bind irq to one or more cores.
 */
int bind_irq_to_core(unsigned int irq, const char *irq_node_name, const char *irq_cpu_name)
{
    int err;
    struct cpumask irq_cpumask;
    cpumask_clear(&irq_cpumask);
    if (irq_node_name == NULL) {
        bind_core_err("irq_node_name is null.\n");
        return -1;
    }
    if (irq_cpu_name == NULL) {
        bind_core_err("irq_cpu_name is null.\n");
        return -1;
    }

    err = get_irq_cpumask(&irq_cpumask, irq_node_name, irq_cpu_name);
    if (err < 0) {
        bind_core_err("get cpumask failed, it will bind default cpus!\n");
        return -1;
    }

    err = irq_set_affinity_hint(irq, &irq_cpumask);
    if (err < 0) {
        bind_core_err("set affinity_hint failed, it will bind default cpus, errcode=%d\n", err);
        return -1;
    }

    bind_core_info("irq %d bind core succ.\n", irq);
    return 0;
}
